# gui_table.py
import sys, os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from PyQt5.QtGui import QColor, QBrush
from PyQt5.QtWidgets import (QApplication, QMainWindow, QAction, QFileDialog,
                             QTableWidget, QTableWidgetItem, QVBoxLayout,
                             QWidget, QToolBar, QStatusBar, QMessageBox, QAbstractItemView, QHeaderView)


class TableViewer(QMainWindow):     # QMainWindow 是「带菜单栏、工具栏、状态栏」的标准主窗口，这里直接继承过来。
    def __init__(self):
        super().__init__()
        self.setWindowTitle("表格阅读器")    # 设置打开的窗口标题和大小
        self.resize(2000, 1200)

        # ---------- 中央表格 ----------
        self.table = QTableWidget()   # QMainWindow 有 5 个预留区域：menuBar、ToolBar、Dock、StatusBar 和 Central Widget（中央控件）
        self.setCentralWidget(self.table)

        # ---------- 菜单栏 ----------
        menubar = self.menuBar()        # menuBar() 第一次被调用时会自动创建菜单栏
        file_menu = menubar.addMenu("文件(&F)")   # &F 让 Alt+F 成为快捷键；

        open_action = QAction("打开(&O)...", self)
        open_action.setShortcut("Ctrl+O")   # 绑定快捷键
        open_action.triggered.connect(self.open_file)
        file_menu.addAction(open_action)

        save_action = QAction("保存(&S)...", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.save_file)
        file_menu.addAction(save_action)

        quit_action = QAction("退出(&X)", self)
        quit_action.triggered.connect(self.close)
        file_menu.addAction(quit_action)

        # ---------- 工具栏 ----------
        toolbar = QToolBar("主工具栏")
        self.addToolBar(toolbar)
        toolbar.addAction(open_action)
        toolbar.addAction(save_action)
        toolbar.addAction(quit_action)

        # ---------- 状态栏 ----------
        self.status = QStatusBar()
        self.setStatusBar(self.status)
        self.status.showMessage("就绪")  # 把提示文字写到左下角，timeout=0表示一直显示

    # ---------- 业务逻辑 ----------
    def open_file(self):
        path,  = QFileDialog.getOpenFileName(
            self,
            "打开表格",     # 弹出打开文件新窗口的标题
            "",
            "Excel (*.xlsx);;CSV (*.csv);;All Files (*)"
        )
        if not path:
            return      # path为空字符串，直接return

        try:
            if path.lower().endswith(".csv"):
                df = pd.read_csv(path)
            else:
                df = pd.read_excel(path)
                df.set_index(df.columns[0], inplace=True)   # 把第一列重新设置为index
        except Exception as e:
            QMessageBox.critical(self, "读取失败", str(e))  # 捕获任何异常提示
            return

        self.populate_table(df)
        self.set_table_color(df)
        self.status.showMessage(f"已加载表格文件：{os.path.basename(path)}")

    def save_file(self):
        # 如果表格还是空的，直接返回
        if self.table.rowCount() == 0:
            QMessageBox.information(self, "提示", "表格为空，无需保存。")
            return

        path, _ = QFileDialog.getSaveFileName(
            self,
            "保存表格",
            "",
            "Excel (*.xlsx);;CSV (*.csv);;All Files (*)"
        )
        if not path:
            return

        # 1) 把 QTableWidget 转成 DataFrame
        rows = self.table.rowCount()        # 返回表格的行数
        cols = self.table.columnCount()     # 返回表格的列数
        header = [self.table.horizontalHeaderItem(c).text() for c in range(cols)]      # 返回表格的列表头

        data = []
        for r in range(rows):
            data.append([
                self.table.item(r, c).text() if self.table.item(r, c) else ""  # 每次新增一行数据
                for c in range(cols)  # item(row, column).text() 用于获取指定单元格的内容
            ])
        df = pd.DataFrame(data, columns=header)

        # 2) 保存
        try:
            if path.lower().endswith(".csv"):
                df.to_csv(path, index=False, encoding='utf-8-sig')
            else:
                df.to_excel(path, index=False)
        except Exception as e:
            QMessageBox.critical(self, "保存失败", str(e))
            return

        self.status.showMessage(f"已保存：{os.path.basename(path)}")

    def populate_table(self, df: pd.DataFrame):
        """DataFrame → QTableWidget"""
        self.table.clear()   # 清空旧数据
        rows, cols = df.shape   # 数据维度
        self.table.setRowCount(rows)    # 设置表格的行数
        self.table.setColumnCount(cols)    # 设置表格的列数
        self.table.setHorizontalHeaderLabels(df.columns.astype(str))    # 设置表格列标题
        self.table.setVerticalHeaderLabels(df.index.astype(str))     # 设置表格行标题
        self.table.setShowGrid(True)

        # 行表头宽度：设一个初始值，并允许交互
        self.table.verticalHeader().setFixedWidth(80)  # index的列宽
        # 列表头高度：设一个初始值，并允许交互
        self.table.horizontalHeader().setFixedHeight(70)  # columns的行高

        # 逐格填充
        for r in range(rows):
            for c in range(cols):
                val = str(df.iat[r, c])
                self.table.setItem(r, c, QTableWidgetItem(val))    # 把单个元素变成QtableWidgetItem塞进去

        # 循环设置行高、列宽
        for i in range(self.table.rowCount()):
            self.table.setRowHeight(i, 50)

        self.table.resizeColumnsToContents()      # 根据内容自动调整列宽，免去用手拉取单元格的宽度
        self.table.setAlternatingRowColors(True)    # 设置交替显示行的颜色

        # 设置表格的编辑触发器为双击启用
        self.table.setEditTriggers(QAbstractItemView.DoubleClicked)

    def set_table_color(self, df):
        table_min = df.min().min()
        table_max = df.max().max()

        PV = table_max - table_min
        cmap = plt.get_cmap('turbo')

        for row_idx in range(self.table.rowCount()):
            for col_idx in range(self.table.columnCount()):
                value = float(self.table.item(row_idx, col_idx).text())
                value_normalized = (value - table_min) / PV
                color = cmap(value_normalized)
                r = int(color[0] * 255)
                g = int(color[1] * 255)
                b = int(color[2] * 255)
                q_color = QColor(r, g, b, 128)
                # 设置当前单元格的颜色
                item = self.table.item(row_idx, col_idx)
                item.setBackground(QBrush(q_color))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = TableViewer()
    win.show()
    sys.exit(app.exec_())



